package com.hxk.juc.collection;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * 多线程情况下操作set集合案例
 */
public class ManyThreadSetCase {

    public static void main(String[] args) {
        //singleThreadSet();

        //manyThreadNotSafe();

        //manyThreadSafe1();

        manyThreadSafe2();

    }

    /**
     * 在单线程情况下使用set，在add时没有任何问题
     */
    private static void singleThreadSet() {
        //HashSet的底层是HashMap，当add时会将add的值放入Map的key中，Map的value中放入了一个固定的常量Object对象，
        // 由于HashMap的默认初始容量为16，动态扩容时是原来的1倍
        Set<String> set = new HashSet<>();
        set.add("a");
        set.add("b");
        set.add("c");
        set.add("d");
        set.forEach(System.out::println);
    }

    /**
     * 在多线程情况下HashSet是线程不安全
     *      在add时会出现ConcurrentModificationException
     */
    private static void manyThreadNotSafe() {
        Set<String> set = new HashSet<>();
        for (int i = 0; i < 30; i++) {
            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(Thread.currentThread().getName()+"\t"+set);
            },String.valueOf(i)).start();
        }
    }

    /**
     *处理多线程下HashSet线程不安全方式1：
     * 使用集合工具类Collections提供的Collections.synchronizedSet(new HashSet<>())方法获取线程安全的HashSet
     *      使用Collections.synchronizedSet(new HashSet<>())方法获取的HashSet，
     *      在添加时方法内部使用synchronized(mutex)锁住了一个Object mutex对象
     *      在这里锁的颗粒度变小了，锁的不是整个对象.
     */
    private static void manyThreadSafe1() {
        Set<String> set = Collections.synchronizedSet(new HashSet<>());
        for (int i = 0; i < 30; i++) {
            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(Thread.currentThread().getName()+"\t"+set);
            }, String.valueOf(i)).start();
        }
    }

    /**
     处理多线程下ArrayList线程不安全方式2：
     * 使用jdk1.5后新加入的juc中的CopyOnWriteArraySet线程安全类
     *      在CopyOnWriteArraySet中的add方法使用了juc中的ReentrantLock（重入锁），
     *      让锁的颗粒度变得更小，目前是性能上最优的解决方案
     */
    private static void manyThreadSafe2(){
        //底层不是HashMap存储方式，使用了Object[]数组存储方式，在add时会对要添加的数据进行是否存在检测，来保证数据的唯一性
        Set<String> set = new CopyOnWriteArraySet<>();
        for (int i = 0; i < 30; i++) {
            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(Thread.currentThread().getName()+"\t"+set);
            },String.valueOf(i)).start();
        }
    }

}
